#![feature(trait_alias)]
#![feature(test)]
extern crate test;

extern crate num_bigint;
#[cfg(test)]
use num_bigint::BigUint;

#[cfg(test)]
extern crate quickcheck;
#[cfg(test)]
#[macro_use(quickcheck)]
extern crate quickcheck_macros;

// Why can't I make this #[cfg(test)] !?
pub mod testlib;

pub mod types;
use types::*;

pub mod util;
#[cfg(test)]
use util::*;

pub mod crypto;
#[cfg(test)]
use crypto::*;

pub mod encode;
#[cfg(test)]
use encode::*;

pub mod decode;
#[cfg(test)]
use decode::*;

pub mod packetizer;
#[cfg(test)]
use packetizer::*;

pub mod grammarfile;
#[cfg(test)]
use grammarfile::*;

#[cfg(feature = "linalg")]
extern crate approx;

/**
 **  *************
 **  *  CHATBOX  * (working title)
 **  *************
 **
 ** * PROOF OF CONCEPT * PROOF OF CONCEPT * PROOF OF CONCEPT * PROOF OF CONCEPT *
 **
 **  Text-based steganographic communication based on stochastic ordered CFG & arithmetic coding ~ proof of concept
 **  Inspired from, and similar to the method described in: https://www.nic.funet.fi/pub/crypt/old/mimic/mimic.text
 **
 **  Shared Parameters: Grammar G, cryptographic secret
 **
 **  encode: encrypted messages -> pseudo-random stream of sentences of G
 **  decode: stream of sentences of G -> maybe encrypted messages
 **
 **/

// CAUTION: experimental software.

// Next steps / ideas:
// - Obtain independent review of the crypto, the message format and everything else
// - Mark some nonterminals as non-coding to make messages somewhat robust against tampering?
// - More ideas?

#[macro_use]
extern crate lazy_static;

#[cfg(test)]
#[macro_use]
extern crate matches;

#[cfg(not(test))]
extern crate serde_yaml;

/*******************************************
 * Iterator utils for message delimitation *
 *******************************************/

pub struct LengthDelimitedInputIterator<'a> {
    iter: &'a mut dyn Iterator<Item = u8>,
    err: Option<anyhow::Error>,
}

impl<'a> LengthDelimitedInputIterator<'a> {
    pub fn new(iter: &'a mut dyn Iterator<Item = u8>) -> Self {
        LengthDelimitedInputIterator { iter, err: None }
    }
}

impl<'a> Iterator for LengthDelimitedInputIterator<'a> {
    type Item = Vec<u8>;

    fn next(&mut self) -> Option<Vec<u8>> {
        let length: Vec<u8> = self
            .iter
            .take_while(|x| *x as char != '\n')
            .collect::<Vec<u8>>();
        let maybe_length = (&String::from_utf8_lossy(&length)).parse::<usize>();

        match maybe_length {
            Err(_) => {
                self.err = Some(anyhow::Error::msg(format!(
                    "could not parse length statement {:?}",
                    length
                )));
                None
            }
            Ok(length) => {
                let block = self.iter.take(length).collect::<Vec<u8>>();

                if self.iter.next() != Some(b'\n') {
                    self.err = Some(anyhow::Error::msg(
                        "input section not \\n-terminated".to_string(),
                    ));
                    None
                } else {
                    Some(block)
                }
            }
        }
    }
}

/**********************
 * Examples for tests *
 **********************/

#[cfg(test)]
mod tests {
    use super::*;

    use crate::testlib::grammars::*;

    use aes::cipher::generic_array::GenericArray;
    use rand::SeedableRng;

    use num_rational::Ratio;
    use num_traits::identities::{One, Zero};

    /// Arbitrary instance of BinaryFraction with reasonable-sized denominator exponents
    impl Arbitrary for BinaryFraction {
        fn arbitrary(g: &mut quickcheck::Gen) -> BinaryFraction {
            BinaryFraction::new(BigUint::arbitrary(g), i16::arbitrary(g) as i64)
        }
    }

    #[test]
    fn decode_no_parse() {
        let grammar = dyck1(0.5, 0.5);

        for input in vec![
            b"(".to_vec(),
            b"ab".to_vec(),
            b"()x".to_vec(),
            b"()()()x".to_vec(),
            b"(()))".to_vec(),
            b"(())())()".to_vec(),
        ]
        .iter()
        {
            let mut p = vec![DecoderStateEntry::new()];

            let k = loop {
                let k = step_decode(&grammar, &mut p, &input);

                if let Ok(DecodeStepResult::Step) = k {
                    continue;
                }
                break k;
            };

            assert_matches!(k, Err(..));
        }
    }

    #[test]
    fn decode_step() {
        let grammar = dyck1(0.5, 0.5);

        for input in vec![
            b"()".to_vec(),
            b"((())())()".to_vec(),
            b"((())((((())())()())())()((())())()())((())())()((())())()()".to_vec(),
            b"()()".to_vec(),
        ]
        .iter()
        {
            let mut p = vec![DecoderStateEntry::new()];

            let k = loop {
                let k = step_decode(&grammar, &mut p, &input);

                assert_matches!(k, Ok(..));

                if let Ok(DecodeStepResult::Step) = k {
                    continue;
                }

                break k;
            };

            assert_matches!(k, Ok(..));
        }
    }

    #[test]
    fn encode_pre_post_finalize() {
        let input = &[0xB5, 0xB0];
        let mut state = EncoderState::new(&input[..]);

        let grammar = dyck1(0.2, 0.7);

        let k = loop {
            let k = step_encode(&grammar, &mut state);
            assert_matches!(k, Ok(..));
            if let Ok(EncodeStepResult::Final(..)) = k {
                break k;
            }
            if let Ok(EncodeStepResult::PreliminaryFinal) = k {
                break k;
            }
        };

        assert_matches!(k, Ok(..));

        let mut random_source = DeterministicPseudoRandoms::new(45);

        if let Ok(EncodeStepResult::PreliminaryFinal) = k {
            let k = loop {
                let k = finalize_encode(&grammar, &mut state, &mut random_source);
                assert_matches!(k, Ok(..));
                if let Ok(EncodeStepResult::Step) = k {
                    continue;
                }
                break k;
            };
            assert_matches!(k, Ok(..));
        }
    }

    #[test]
    fn prefixfree_decoder_negative() {
        for grammar in vec![
            wrapped_nonempty_dyck1(0.25, 0.75),
            wrapped_nonempty_dyck1(0.4, 0.5),
        ] {
            for input in vec![
                b"()".to_vec(),
                b"()()".to_vec(),
                b"(())()".to_vec(),
                b"(())())".to_vec(),
                b"(".to_vec(),
                b"(())(".to_vec(),
                b"(()))".to_vec(),
                b"(()())(()())(()())()".to_vec(),
                b"(()())(()())(()())()(()())".to_vec(),
            ]
            .iter()
            {
                let mut inputstream_for_decoder = input.into_iter().map(|x| x.clone());
                let mut dec = PrefixfreeDecoder::new(&grammar, &mut inputstream_for_decoder);
                let decoded = dec.decode();
                assert_matches!(decoded, Err(..));
            }
        }
    }

    #[test]
    fn prefixfree_decoder() {
        for grammar in vec![
            wrapped_nonempty_dyck1(0.25, 0.75),
            wrapped_nonempty_dyck1(0.4, 0.5),
        ] {
            for input in vec![
                b"(())".to_vec(),
                b"((()))".to_vec(),
                b"(())(())".to_vec(),
                b"(()())".to_vec(),
                b"(()())(()())(()())(())".to_vec(),
            ]
            .iter()
            {
                let mut inputstream_for_decoder = input.into_iter().map(|x| x.clone());
                let mut dec = PrefixfreeDecoder::new(&grammar, &mut inputstream_for_decoder);
                let decoded = dec.decode();
                assert_matches!(decoded, Ok(..));
            }
        }
    }

    #[test]
    fn prefixfree_decoder_as_iterator() {
        for grammar in vec![
            wrapped_nonempty_dyck1(0.25, 0.75),
            wrapped_nonempty_dyck1(0.4, 0.5),
        ] {
            for (input, number) in vec![
                (b"(())".to_vec(), 1),
                (b"((()))".to_vec(), 1),
                (b"(()())".to_vec(), 1),
                (b"(())(())".to_vec(), 2),
                (b"(()())(()())(()())(())".to_vec(), 4),
            ]
            .iter()
            {
                let mut inputstream_for_decoder = input.into_iter().map(|x| x.clone());
                let mut dec = PrefixfreeDecoder::new(&grammar, &mut inputstream_for_decoder);
                for _i in 0..*number {
                    let res = dec.next();
                    assert_matches!(res, Some(..));
                }
                let res = dec.next();
                assert_matches!(res, None);
            }
        }
    }

    #[quickcheck]
    fn encode_decode_qc(input: Vec<u8>) -> bool {
        // concerns all quickcheck tests that encode an "Arbitrary" vec: input must not be empty
        if input.len() == 0 {
            return true;
        }
        // want quick test
        if input.len() > 20 {
            return true;
        }

        let grammar = linear_with_powers_of_2();
        let random_source = Box::new(DeterministicPseudoRandoms::new(45));
        let enc = Encoder::new(&grammar, &input[..], random_source);

        let encoded: Vec<Vec<u8>> = enc.collect();

        let mut encoded_iter = encoded.into_iter();
        let mut dec = Decoder::new(&grammar, &mut encoded_iter);
        let res = dec.decode();

        let res = res.unwrap();

        let rb = binaryfraction_between_0_and_1_to_bytes(&res.l);

        rb.len() >= input.len() && &input[..] == &rb[0..input.len()]
    }

    #[test]
    fn encode_decode() {
        let grammar = linear_with_powers_of_2();

        for input in vec![
            vec![0x03],
            vec![0x00, 0x00, 0x00],
            vec![0x01],
            vec![0x02],
            vec![0x00],
            vec![0x02],
            vec![0x00, 0x04],
            vec![0x00, 0x00, 0x0E],
            vec![0x00, 0x00, 0x0F],
            vec![0x80],
            vec![0x8F],
            b"message".to_vec(),
            b"ceci n'est pas un message".to_vec(),
            vec![0x1, 128],
        ]
        .iter()
        {
            let random_source = Box::new(DeterministicPseudoRandoms::new(45));
            let enc = Encoder::new(&grammar, &input[..], random_source);

            let encoded: Vec<Vec<u8>> = enc.collect();
            assert_ne!(encoded.len(), 0);

            let mut encoded_iter = encoded.into_iter();
            let mut dec = Decoder::new(&grammar, &mut encoded_iter);
            let res = dec.decode();

            assert_matches!(res, Ok(..));
            let res = res.unwrap();

            let rb = binaryfraction_between_0_and_1_to_bytes(&res.l);

            assert_eq!(&input[..], &rb[0..input.len()]);
        }
    }

    #[test]
    fn encode_decode_lindec() {
        let grammar = linear_decimal();

        for input in vec![
            vec![
                4, 199, 149, 115, 179, 50, 241, 230, 125, 250, 93, 78, 140, 117, 238, 195, 33, 229,
                191, 173, 241, 234, 146, 141, 125, 249, 255, 42, 189, 189, 229, 16, 77, 210, 107,
                243, 41, 39, 14, 150, 163, 75, 149, 211, 46, 56, 12, 91, 101, 187, 159, 215, 147,
                203, 78, 94, 48, 123, 128, 35, 102, 223, 111, 125, 253, 124, 147, 119, 15, 168,
                138, 57, 232, 204, 97, 118, 93, 68, 30, 130, 217, 60, 176, 207, 218, 221, 174, 228,
                252, 210, 1, 235, 70, 205, 241, 119, 28, 56, 68, 193, 235, 61, 203, 2, 199, 210,
                149, 15, 164, 243, 180, 44, 11, 237, 11, 241, 129, 37, 116, 0, 60, 228, 243, 3,
                159, 172, 194, 4, 103, 247, 24, 172, 18, 215, 47, 131, 49, 140, 32, 27, 205, 181,
                34, 52,
            ],
            vec![0x03],
            vec![0x00, 0x00, 0x00],
            vec![0x01],
            vec![0x02],
            vec![0x00],
            vec![0x02],
            vec![0x00, 0x04],
            vec![0x00, 0x00, 0x0E],
            vec![0x00, 0x00, 0x0F],
            vec![0x80],
            vec![0x8F],
            b"message".to_vec(),
            b"ceci n'est pas un message".to_vec(),
            vec![0x1, 128],
        ]
        .iter()
        {
            let random_source = Box::new(DeterministicPseudoRandoms::new(45));
            let enc = Encoder::new(&grammar, &input[..], random_source);

            let encoded: Vec<Vec<u8>> = enc.collect();
            assert_ne!(encoded.len(), 0);

            let mut encoded_iter = encoded.into_iter();

            let mut dec = Decoder::new(&grammar, &mut encoded_iter);
            let res = dec.decode();

            assert_matches!(res, Ok(..));
            let res = res.unwrap();
            let rb = binaryfraction_between_0_and_1_to_bytes(&res.l);

            assert_eq!(&input[..], &rb[0..input.len()]);
        }
    }

    #[test]
    fn packetizer_whole() {
        use sha3::digest::generic_array::{typenum::U32, GenericArray};

        let miniseed = 45;
        let stream_id = b"XYZZYXYZ";

        let mut inputs_iter = vec![
            vec![0x1, 64],
            b"\x00\xff%!)(*(@)_@(#)*_@!)%*(".to_vec(),
            b"AAAAAAAAAAAAAAAAaaaaaaaaaaaaaaaa".to_vec(),
            b"barfoo".to_vec(),
        ]
        .into_iter();
        let key: &GenericArray<u8, U32> = GenericArray::from_slice(&[0u8; 32]);

        let mut p = Packetizer {
            rng: rand_chacha::ChaCha20Rng::seed_from_u64(miniseed),
            aes_key: *key,
            stream_id,
            messages_iter: &mut inputs_iter,
            sequence_no: 0,
        };

        let packet = p.next();
        assert_matches!(packet, Some(..));

        if let Some(stuff) = packet {
            let mx = try_read_packet_header(key, &stuff[..]);
            assert_matches!(mx, Ok(..));

            let mx = try_read_packet(key, &stuff[..]);
            assert_matches!(mx, Ok(..));
        }
    }

    /** Packetize / Depacketize and Crypto */

    #[derive(Debug, Clone)]
    struct Key32 {
        pub data: Box<[u8; 32]>,
    }

    use core::cmp;

    impl PartialEq for Key32 {
        #[inline]
        fn eq(&self, other: &Self) -> bool {
            (*self.data).cmp(&*other.data) == cmp::Ordering::Equal
        }
    }

    impl Eq for Key32 {}

    use num_traits::FromPrimitive;
    use quickcheck::Arbitrary;
    use std::convert::TryInto;

    impl Arbitrary for Key32 {
        fn arbitrary(g: &mut quickcheck::Gen) -> Key32 {
            let size = 32;
            Key32 {
                data: Box::new(
                    (&(0..size)
                        .map(|_| Arbitrary::arbitrary(g))
                        .collect::<Vec<u8>>()[..size])
                        .try_into()
                        .expect("slice with incorrect length"),
                ),
            }
        }
    }

    #[quickcheck]
    fn hmac_distinct(key1: Key32, key2: Key32, msg1: Vec<u8>, msg2: Vec<u8>) -> bool {
        let antecedent = key1 != key2 || msg1 != msg2;
        let conclusion = hmac_sha3(&*key1.data, &msg1) != hmac_sha3(&*key2.data, &msg2);
        !antecedent || conclusion
    }

    // regression 1
    #[test]
    fn knaxus() {
        let input = vec![
            4, 199, 149, 115, 179, 50, 241, 230, 125, 250, 93, 78, 140, 117, 238, 195, 33, 229,
            191, 173, 241, 234, 146, 141, 125, 249, 255, 42, 189, 189, 229, 16, 77, 210, 107, 243,
            41, 39, 14, 150, 163, 75, 149, 211, 46, 56, 12, 91, 101, 187, 159, 215, 147, 203, 78,
            94, 48, 123, 128, 35, 102, 223, 111, 125, 253, 124, 147, 119, 15, 168, 138, 57, 232,
            204, 97, 118, 93, 68, 30, 130, 217, 60, 176, 207, 218, 221, 174, 228, 252, 210, 1, 235,
            70, 205, 241, 119, 28, 56, 68, 193, 235, 61, 203, 2, 199, 210, 149, 15, 164, 243, 180,
            44, 11, 237, 11, 241, 129, 37, 116, 0, 60, 228, 243, 3, 159, 172, 194, 4, 103, 247, 24,
            172, 18, 215, 47, 131, 49, 140, 32, 27, 205, 181, 34, 52,
        ];
        let grammar = linear_decimal();

        let packetizer = vec![input.clone()].into_iter();

        let encoded_packets_iterator = packetizer
            .map(|packet| {
                let encoder = Encoder::with_live_random(&grammar, &packet[..]);
                let result = encoder.collect::<Vec<_>>();
                result
            })
            .flatten();

        let bounds_iterator = encoded_packets_iterator
            .filter_map(|w| {
                let inputs = vec![w];
                let mut iter = inputs.into_iter();
                let mut dec = Decoder::new(&grammar, &mut iter);
                match dec.decode() {
                    Ok(ok) => Some(ok),
                    _ => None,
                }
            })
            .into_iter();

        let bds: Vec<BoundsLP> = bounds_iterator.collect();

        let combined_fragments = combine_fragments(&bds[..]);

        let result = combined_fragments.clone();

        let input_hyp = binaryfraction_between_0_and_1_to_bytes(&result.l);

        // TODO deduplicate code

        let upper = result.l.clone() + result.p.clone();
        let knownbits = binaryfractions_length_common_prefix(&result.l, &upper);

        let knownbytes = match knownbits {
            BinaryFractionLongestCommonPrefix::Same => todo!(),
            BinaryFractionLongestCommonPrefix::Invalid => todo!(),
            BinaryFractionLongestCommonPrefix::Length { bits } => bits / 8,
        } as usize;

        let m = std::cmp::min(input.len(), input_hyp.len());

        assert_eq!(knownbytes >= input.len(), true);
        assert_eq!(input_hyp.len() >= input.len(), true);

        assert_eq!(&input[..m], &input_hyp[..m]);
        assert_eq!(&input, &input_hyp[..input.len()]);
    }

    #[quickcheck]
    fn test_binaryfraction_length_common_prefix_same(k: BigUint, plusf: u8) -> bool {
        let r = BinaryFraction::new(k.clone(), plusf as i64 + (k.to_bytes_le().len() * 8) as i64);
        binaryfractions_length_common_prefix(&r, &r) == BinaryFractionLongestCommonPrefix::Same
    }

    #[quickcheck]
    fn test_binaryfraction_length_common_prefix_differ_last(k: BigUint, plusf: u8) {
        let l: BigUint = k.clone() ^ &One::one();
        let f = 1 + plusf as i64 + (k.to_bytes_le().len() * 8) as i64;
        let r = BinaryFraction::new(k.clone(), f);
        let s = BinaryFraction::new(l.clone(), f);

        assert_eq!(
            binaryfractions_length_common_prefix(&r, &s),
            BinaryFractionLongestCommonPrefix::Length { bits: f as u32 - 1 }
        )
    }

    #[quickcheck]
    fn test_binaryfraction_length_common_prefix_differ_last_1(plusf: u8) {
        let f = 1 + 10 * plusf as i64 + 1 as i64;
        let r = BinaryFraction::new(One::one(), f);
        let s = BinaryFraction::new(One::one(), f + 1);

        assert_eq!(
            binaryfractions_length_common_prefix(&r, &s),
            BinaryFractionLongestCommonPrefix::Length { bits: f as u32 - 1 }
        )
    }

    #[test]
    fn test_binaryfraction_length_common_prefix() {
        let r = BinaryFraction::new(BigUint::from_u32(1).unwrap(), 1);
        let s = BinaryFraction::new(BigUint::from_u32(1).unwrap(), 2);
        assert_eq!(
            binaryfractions_length_common_prefix(&r, &s),
            BinaryFractionLongestCommonPrefix::Length { bits: 0 }
        );

        let r = BinaryFraction::new(BigUint::from_u32(1).unwrap(), 1);
        let s = BinaryFraction::new(BigUint::from_u32(1).unwrap(), 3);
        assert_eq!(
            binaryfractions_length_common_prefix(&r, &s),
            BinaryFractionLongestCommonPrefix::Length { bits: 0 }
        );

        let r = BinaryFraction::new(BigUint::from_u32(1).unwrap(), 2);
        let s = BinaryFraction::new(BigUint::from_u32(1).unwrap(), 3);
        assert_eq!(
            binaryfractions_length_common_prefix(&r, &s),
            BinaryFractionLongestCommonPrefix::Length { bits: 1 }
        );

        let r = BinaryFraction::new(BigUint::from_u32(2).unwrap(), 2);
        let s = BinaryFraction::new(BigUint::from_u32(3).unwrap(), 2);
        assert_eq!(
            binaryfractions_length_common_prefix(&r, &s),
            BinaryFractionLongestCommonPrefix::Length { bits: 1 }
        );

        let r = BinaryFraction::new(BigUint::from_u32(0xFF).unwrap(), 8);
        let s = BinaryFraction::new(BigUint::from_u32(0x7F).unwrap(), 8);
        assert_eq!(
            binaryfractions_length_common_prefix(&r, &s),
            BinaryFractionLongestCommonPrefix::Length { bits: 0 }
        );

        let r = BinaryFraction::new(BigUint::from_u32(0xFF).unwrap(), 8);
        let s = BinaryFraction::new(BigUint::from_u32(0xEF).unwrap(), 8);
        assert_eq!(
            binaryfractions_length_common_prefix(&r, &s),
            BinaryFractionLongestCommonPrefix::Length { bits: 3 }
        );

        let r = BinaryFraction::new(BigUint::from_u32(0xFF).unwrap(), 8);
        let s = BinaryFraction::new(BigUint::from_u32(0xFE).unwrap(), 8);
        assert_eq!(
            binaryfractions_length_common_prefix(&r, &s),
            BinaryFractionLongestCommonPrefix::Length { bits: 7 }
        );

        let r = BinaryFraction::new(BigUint::from_u32(0xFF).unwrap(), 16);
        let s = BinaryFraction::new(BigUint::from_u32(0xFE).unwrap(), 16);
        assert_eq!(
            binaryfractions_length_common_prefix(&r, &s),
            BinaryFractionLongestCommonPrefix::Length { bits: 15 }
        );

        let r = BinaryFraction::new(BigUint::from_u32(0xFF).unwrap(), 86);
        let s = BinaryFraction::new(BigUint::from_u32(0xFE).unwrap(), 86);
        assert_eq!(
            binaryfractions_length_common_prefix(&r, &s),
            BinaryFractionLongestCommonPrefix::Length { bits: 85 }
        );

        let r = BinaryFraction::new(BigUint::from_u32(0xFF).unwrap(), 8);
        let s = BinaryFraction::new(BigUint::from_u32(0xFE).unwrap(), 86);
        assert_eq!(
            binaryfractions_length_common_prefix(&r, &s),
            BinaryFractionLongestCommonPrefix::Length { bits: 0 }
        );

        let r = BinaryFraction::new(BigUint::from_u32(0xFF).unwrap(), 8);
        let s = BinaryFraction::new(BigUint::from_u32(0xFE).unwrap(), 86) + r.clone();
        assert_eq!(
            binaryfractions_length_common_prefix(&r, &s),
            BinaryFractionLongestCommonPrefix::Length { bits: 78 }
        );
    }

    #[test]
    fn packetize_depacketize() {
        let my_key = vec![13u8; *KEY_SIZE];

        // Test involves encode/decode because trial decode needs it
        for grammar in vec![
            linear_decimal(),
            linear_with_powers_of_2(),
            easy2_prefixfree(),
            // still too slow for quick test?
            // , linear_decimal(), dyck1(0.2, 0.8)
        ]
        .into_iter()
        {
            for input in vec![
                // vec![0u8; 99],
                vec![1u8; 128],
                vec![99u8; 228],
                vec![13u8; 428],
                b"ceci n'est pas un message: lorem ipsum sic dolor amet".to_vec(),
                b"ceci n'es".to_vec(),
            ] {
                // now it's going to take even longer ...
                // for chunk_size in vec![4, 16, 43, 309].into_iter() {
                for chunk_size in vec![4, 16, 43].into_iter() {
                    // arbitrary >0, test different sizes

                    let stream_id: [u8; 8] = Default::default();
                    let rng = rand_chacha::ChaCha20Rng::seed_from_u64(23);

                    use itertools::Itertools;
                    let chunks = input.iter().chunks(chunk_size);
                    let mut it = chunks.into_iter().map(|chunk| chunk.cloned().collect());

                    let packetizer =
                        Packetizer::new(&my_key[0..*KEY_SIZE], &mut it, &stream_id, rng);

                    let encoded_packets_iterator = packetizer
                        .map(|packet| {
                            let encoder = Encoder::with_live_random(&grammar, &packet[..]);
                            let result = encoder.collect::<Vec<_>>();
                            result
                        })
                        .flatten();

                    let bounds_iterator = encoded_packets_iterator
                        .filter_map(|w| {
                            let inputs = vec![w];
                            let mut iter = inputs.into_iter();
                            let mut dec = Decoder::new(&grammar, &mut iter);
                            match dec.decode() {
                                Ok(ok) => Some(ok),
                                _ => None,
                            }
                        })
                        .into_iter();

                    let depacketizer = DecodingLoop::new(
                        Box::new(bounds_iterator),
                        Box::new(|fragments| {
                            let trial_hdr = trial_decode_header_from_fragments(
                                &my_key[0..*KEY_SIZE],
                                fragments,
                            );
                            trial_hdr
                        }),
                        Box::new(|size, fragments| {
                            let trial_msg =
                                trial_decode_from_fragments(&my_key[0..*KEY_SIZE], size, fragments);
                            trial_msg
                        }),
                    );

                    // for depac in depacketizer {
                    //      println!("{:?}", depac);
                    //      println!("{}", String::from_utf8_lossy(&depac.payload));
                    // }

                    let result: Vec<u8> = depacketizer
                        .map(|packet| packet.payload)
                        .flatten()
                        .collect();

                    assert_eq!(input, result);
                }
            }
        }
    }

    #[test]
    #[should_panic(expected = "bigrational_between_0_and_1_to_bytes: input 3 out of range")]
    fn test_invalid_bigrationals_3() {
        bigrational_between_0_and_1_to_bytes(&Ratio::new(
            BigUint::from(3 as u32),
            BigUint::from(1 as u32),
        ));
    }

    #[test]
    #[should_panic(expected = "bigrational_between_0_and_1_to_bytes: input 1 out of range")]
    fn test_invalid_bigrationals_1() {
        bigrational_between_0_and_1_to_bytes(&Ratio::new(
            BigUint::from(1 as u32),
            BigUint::from(1 as u32),
        ));
    }

    #[test]
    #[should_panic(expected = "bigrational_between_0_and_1_to_bytes: input 0 out of range")]
    fn test_invalid_bigrationals_0() {
        bigrational_between_0_and_1_to_bytes(&Ratio::new(
            BigUint::from(0 as u32),
            BigUint::from(1 as u32),
        ));
    }

    #[test]
    #[should_panic(expected = "binaryfraction_between_0_and_1_to_bytes: input 0 out of range")]
    fn test_invalid_binaryfraction_between_0_and_1_to_bytes_0() {
        let a = BinaryFraction::new(Zero::zero(), 0);
        binaryfraction_between_0_and_1_to_bytes(&a);
    }

    #[test]
    #[should_panic(expected = "binaryfraction_between_0_and_1_to_bytes: input 1 out of range")]
    fn test_invalid_binaryfraction_between_0_and_1_to_bytes_1() {
        let a = BinaryFraction::new(One::one(), 0);
        binaryfraction_between_0_and_1_to_bytes(&a);
    }

    #[test]
    #[should_panic(expected = "binaryfraction_between_0_and_1_to_bytes: input 3 out of range")]
    fn test_invalid_binaryfraction_between_0_and_1_to_bytes_3() {
        let a = BinaryFraction::new(BigUint::from(3 as u32), 0);
        binaryfraction_between_0_and_1_to_bytes(&a);
    }

    #[cfg(feature = "linalg")]
    #[test]
    fn lcm_usize() {
        assert_eq!(1, num_utils::lcm(&[1usize]));
        assert_eq!(2, num_utils::lcm(&[1usize, 2]));
        assert_eq!(6, num_utils::lcm(&[1usize, 2, 3]));
        assert_eq!(12, num_utils::lcm(&[1usize, 2, 3, 4]));
        assert_eq!(60, num_utils::lcm(&[1usize, 2, 3, 4, 5]));
        assert_eq!(60, num_utils::lcm(&[1usize, 2, 3, 4, 5, 6]));
        assert_eq!(420, num_utils::lcm(&[1usize, 2, 3, 4, 5, 6, 7]));
        assert_eq!(840, num_utils::lcm(&[1usize, 2, 3, 4, 5, 6, 7, 8]));
        assert_eq!(2520, num_utils::lcm(&[1usize, 2, 3, 4, 5, 6, 7, 8, 9]));
        assert_eq!(2520, num_utils::lcm(&[1usize, 2, 3, 4, 5, 6, 7, 8, 9, 10]));
        assert_eq!(
            27720,
            num_utils::lcm(&[1usize, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
        );
        assert_eq!(
            27720,
            num_utils::lcm(&[1usize, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
        );
        assert_eq!(
            27720 * 13,
            num_utils::lcm(&[1usize, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13])
        );
        assert_eq!(
            27720 * 13,
            num_utils::lcm(&[1usize, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
        );
        assert_eq!(
            27720 * 13,
            num_utils::lcm(&[1usize, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
        );
    }

    #[cfg(feature = "linalg")]
    #[test]
    fn grammars_linalg_checks() {
        use super::grammarfile::linalg::{
            compute_expansion_matrix, estimate_dominant_eigenvalue,
        };
        extern crate nalgebra as na;

        use na::{Matrix1x2, RowVector2};

        let grammar = dyck1(0.5, 0.5);
        let a: na::Matrix<_, _, _, _> = compute_expansion_matrix(&grammar).into();

        // weighted ways of getting "S" are 0.5 on average from each "Z", and 0.5 on average from each "S".
        assert_eq!(
            a.row(0).into_owned(),
            Matrix1x2::from_rows(&[RowVector2::new(0.5, 0.5)])
        );
        // of getting "Z" - it's guaranteed from "S" with 1 "Z" for each rule, and "Z"s do not make more "Z"s directly.
        assert_eq!(
            a.row(1).into_owned(),
            Matrix1x2::from_rows(&[RowVector2::new(1.0, 0.0)])
        );

        let lambda_1 = estimate_dominant_eigenvalue(&grammar);
        assert!(lambda_1 > 0.0);

        let grammar = dyck1(0.75, 0.5);
        let lambda_1 = estimate_dominant_eigenvalue(&grammar);
        assert!(lambda_1 > 0.0);

        let grammar = dyck1(0.2, 0.8);
        let lambda_1 = estimate_dominant_eigenvalue(&grammar);
        assert!(lambda_1 > 0.0);
    }

    #[test]
    fn bigrationals_and_binaryfractions() {
        // Do BinaryFractions behave exactly the same as Ratio<BigUint> with power of 2 denomiators?
    }

    #[quickcheck]
    fn binaryfraction_to_from_bigrational(bf: BinaryFraction) -> bool {
        let r = BinaryFraction::into_bigrational(bf.clone());
        let y = BinaryFraction::from_bigrational(r);
        bf == y
    }

    #[quickcheck]
    fn same_binaryfraction_same_prefix(r: BinaryFraction, bits: u8) -> bool {
        binaryfractions_same_prefix(bits as u32, &r, &r)
    }

    #[test]
    fn binaryfraction_same_prefix() {
        let bf1 = BinaryFraction::new_raw(BigUint::from_u32(3).unwrap(), 5);
        let bf2 = BinaryFraction::new_raw(BigUint::from_u32(1).unwrap(), 4);
        assert!(binaryfractions_same_prefix(0, &bf1, &bf2));
        assert!(binaryfractions_same_prefix(1, &bf1, &bf2));
        assert!(binaryfractions_same_prefix(2, &bf1, &bf2));
        assert!(binaryfractions_same_prefix(3, &bf1, &bf2));
        assert!(binaryfractions_same_prefix(4, &bf1, &bf2));
        assert!(!binaryfractions_same_prefix(5, &bf1, &bf2));
        assert!(!binaryfractions_same_prefix(6, &bf1, &bf2));
    }

    #[quickcheck]
    fn binaryfraction_cmp_self(bf: BinaryFraction) -> bool {
        let check1 = bf == bf;
        check1
    }

    #[quickcheck]
    fn binaryfraction_from_biguint_1(k: BigUint) -> bool {
        let bf1 = BinaryFraction::new(k.clone(), 0);
        let bf2 = BinaryFraction::new_raw(k, 0);
        bf1 == bf2
    }

    #[quickcheck]
    fn binaryfraction_from_biguint_2(k: BigUint) -> bool {
        let bf1 = BinaryFraction::new(k.clone(), 0);
        let bf3 = BinaryFraction::from_biguint(k);
        bf3 == bf1
    }

    #[quickcheck]
    fn binaryfraction_from_biguint_3(k: BigUint) -> bool {
        let bf3 = BinaryFraction::from_biguint(k);
        bf3.clone().trunc() == bf3
    }

    #[quickcheck]
    fn binaryfraction_addassign_reduced(mut bf1: BinaryFraction, bf2: BinaryFraction) -> bool {
        bf1 += bf2;
        bf1.k == bf1.reduced().k && bf1.f == bf1.reduced().f
    }

    #[quickcheck]
    fn binaryfraction_adds_reduced(bf1: BinaryFraction, bf2: BinaryFraction) -> bool {
        let bf = bf1 + bf2.clone() + &bf2.clone();
        bf.k == bf.reduced().k && bf.f == bf.reduced().f
    }

    #[quickcheck]
    fn binaryfraction_subassign_reduced(mut bf1: BinaryFraction, bf2: BinaryFraction) -> bool {
        if bf1 < bf2 {
            return true;
        }
        bf1 -= bf2;
        bf1.k == bf1.reduced().k && bf1.f == bf1.reduced().f
    }

    #[quickcheck]
    fn binaryfraction_subs_reduced(bf1: BinaryFraction, bf2: BinaryFraction) -> bool {
        if bf1 < bf2.clone() + &bf2 {
            return true;
        }
        let bf = bf1 - bf2.clone() - &bf2.clone();
        bf.k == bf.reduced().k && bf.f == bf.reduced().f
    }

    #[quickcheck]
    fn binaryfraction_mulassign_reduced(mut bf: BinaryFraction) -> bool {
        bf *= bf.clone();
        bf.k == bf.reduced().k && bf.f == bf.reduced().f
    }

    #[quickcheck]
    fn binaryfraction_mul_reduced(bf: BinaryFraction) -> bool {
        let bf = bf.clone() * bf.clone() * &bf.clone();
        bf.k == bf.reduced().k && bf.f == bf.reduced().f
    }

    #[quickcheck]
    fn binaryfraction_add_comm(a: BinaryFraction, b: BinaryFraction) -> bool {
        let r1 = a.clone() + &b;
        let r2 = b + a;
        r1 == r2
    }

    #[quickcheck]
    fn binaryfraction_mul_comm(a: BinaryFraction, b: BinaryFraction) -> bool {
        let r1 = a.clone() * &b;
        let r2 = b * a;
        r1 == r2
    }

    #[quickcheck]
    fn binaryfraction_add_zero(a: BinaryFraction) -> bool {
        let o: BinaryFraction = Zero::zero();
        let r1: BinaryFraction = o + &a;
        r1 == a
    }

    #[quickcheck]
    fn binaryfraction_mul_zero(a: BinaryFraction) -> bool {
        let o: BinaryFraction = Zero::zero();
        let r1: BinaryFraction = a * o;
        r1.is_zero()
    }

    #[quickcheck]
    fn binaryfraction_mul_one(a: BinaryFraction) -> bool {
        let e: BinaryFraction = One::one();
        let r1: BinaryFraction = a.clone() * &e;
        e.is_one() && r1 == a
    }

    #[quickcheck]
    fn binaryfraction_add_sub(a: BinaryFraction, b: BinaryFraction) -> bool {
        a == (a.clone() + b.clone()) - b
    }

    #[test]
    fn binaryfraction_one_zero() {
        let zero: BinaryFraction = Zero::zero();
        assert!(zero.is_zero());
        assert!(!zero.is_one());
        let one: BinaryFraction = One::one();
        assert!(one.is_one());
        assert!(!one.is_zero());
        let half = BinaryFraction::new(BigUint::from(1u64), 1);
        assert!(!half.is_one());
        assert!(!half.is_zero());
        let two = BinaryFraction::new(BigUint::from(1u64), -1);
        assert!(!two.is_one());
        assert!(!two.is_zero());
    }

    #[quickcheck]
    fn binaryfraction_add_mul_ldistrib(
        a: BinaryFraction,
        b: BinaryFraction,
        c: BinaryFraction,
    ) -> bool {
        let r1 = a.clone() * (b.clone() + &c);
        let r2 = a.clone() * b + a * c;
        r1 == r2
    }

    #[quickcheck]
    fn binaryfraction_add_mul_rdistrib(
        a: BinaryFraction,
        b: BinaryFraction,
        c: BinaryFraction,
    ) -> bool {
        let r1 = (b.clone() + &c) * a.clone();
        let r2 = b * a.clone() + c * a;
        r1 == r2
    }

    #[quickcheck]
    fn binaryfraction_add_cmp(a: BinaryFraction, b: BinaryFraction, c: BinaryFraction) -> bool {
        let r1 = a < b;
        let r2 = a + &c < b + &c;
        r1 == r2
    }

    /// Holds because BinaryFraction only represents positive numbers
    #[quickcheck]
    fn binaryfraction_mul_cmp(a: BinaryFraction, b: BinaryFraction, c: BinaryFraction) -> bool {
        let r0 = c > Zero::zero();
        let r1 = a < b;
        let r2 = a * &c <= b * &c;
        !(r0 && r1) || r2
    }

    #[quickcheck]
    fn binaryfraction_bigrational_mul_same(a: BinaryFraction, b: BinaryFraction) -> bool {
        let y = a.clone() * b.clone();

        let ra = BinaryFraction::into_bigrational(a.clone());
        let rb = BinaryFraction::into_bigrational(b.clone());

        let r1 = ra.clone() * rb.clone();

        let ya = BinaryFraction::from_bigrational(ra);
        let yb = BinaryFraction::from_bigrational(rb);

        let y1 = ya.clone() * yb.clone();

        y1 == y && r1 == BinaryFraction::into_bigrational(y1)
    }

    #[quickcheck]
    fn binaryfraction_bigrational_add_same(a: BinaryFraction, b: BinaryFraction) -> bool {
        let y = a.clone() + b.clone();

        let ra = BinaryFraction::into_bigrational(a.clone());
        let rb = BinaryFraction::into_bigrational(b.clone());

        let r1 = ra.clone() + rb.clone();

        let ya = BinaryFraction::from_bigrational(ra);
        let yb = BinaryFraction::from_bigrational(rb);

        let y1 = ya.clone() + yb.clone();

        y1 == y && r1 == BinaryFraction::into_bigrational(y1)
    }

    #[quickcheck]
    fn bigrationals_and_binaryfractions_and_bytevecs_same_1(input: Vec<u8>) -> bool {
        // input must not be empty
        if input.len() == 0 {
            return true;
        }
        let r = bytes_to_bigrational_between_0_and_1(&input[..]);
        let y = bytes_to_binaryfraction_between_0_and_1(&input[..]);
        r == BinaryFraction::into_bigrational(y)
    }

    #[quickcheck]
    fn bigrationals_and_binaryfractions_leadingzeroes_same(a: BinaryFraction) -> bool {
        // assert input in valid input range, has at least one "1"
        if a.is_zero() {
            return true;
        };
        let r = BinaryFraction::into_bigrational(a.clone());
        leading_zeroes(&r) == binaryfraction_leading_zeroes(&a)
    }

    #[test]
    fn test_binaryfraction_leading_zeroes() {
        let r = BinaryFraction::new(One::one(), 1);
        assert_eq!(binaryfraction_leading_zeroes(&r), Some(0));
        let r = BinaryFraction::new(One::one(), 2);
        assert_eq!(binaryfraction_leading_zeroes(&r), Some(1));
        let r = BinaryFraction::new(Zero::zero(), 2);
        assert_eq!(binaryfraction_leading_zeroes(&r), None);

        let r1 = BinaryFraction::new(One::one(), 2);
        let r2 = BinaryFraction::new(One::one(), 3);
        let r3 = r1 + r2;
        assert_eq!(binaryfraction_leading_zeroes(&r3), Some(1));
    }

    /**
     fa = exponent of denominator (ka / 2^{fa})
    */
    #[quickcheck]
    fn bigrationals_and_binaryfractions_and_bytevecs_same_2(a: BinaryFraction) -> bool {
        // assert input in valid input range, has at least one "1"
        if a.is_zero() {
            return true;
        };
        if a >= One::one() {
            return true;
        }
        let r = BinaryFraction::into_bigrational(a.clone());
        let va = binaryfraction_between_0_and_1_to_bytes(&a);
        let vr = bigrational_between_0_and_1_to_bytes(&r);
        va == vr
    }

    #[quickcheck]
    fn util_xor_properties(v1: Vec<u8>, v2: Vec<u8>) -> bool {
        let l = std::cmp::min(v1.len(), v2.len());
        let v12 = xor_blocks(&v1[0..l], &v2[0..l]);
        let v21 = xor_blocks(&v2[0..l], &v1[0..l]);
        let v122 = xor_blocks(&v12[0..l], &v2[0..l]);
        let zeroes1 = v1[0..l]
            .iter()
            .filter(|x| **x != 0)
            .collect::<Vec<_>>()
            .is_empty();
        v12 == v21 && zeroes1 == (v12[0..l] == v2[0..l]) && v122[0..l] == v1[0..l]
    }

    #[test]
    fn bigrationals_and_bytevecs() {
        for input in vec![
            vec![0x00, 0x00],
            vec![0x00, 0x00, 0x00],
            vec![0x00, 0x00, 0x00, 0x02],
            vec![0x00, 0x00, 0x00, 0x80],
            vec![0x00, 0x00, 0xFF, 0xFF],
            vec![0x00, 0x00, 0xFF, 0xFE],
            vec![0xFF, 0xFF, 0x00, 0x00],
            vec![
                vec![0x00 as u8],
                vec![0xFF as u8],
                b"foobar".to_vec(),
                vec![0xFF as u8],
                vec![0x02 as u8],
            ]
            .concat(),
            vec![
                vec![0x00 as u8],
                vec![0xFF as u8],
                b"foobar".to_vec(),
                vec![0xFF as u8],
                vec![0x03 as u8],
            ]
            .concat(),
            vec![
                vec![0x00 as u8],
                vec![0xFF as u8],
                b"foobar".to_vec(),
                vec![0x00 as u8],
                vec![0x00 as u8],
            ]
            .concat(),
        ]
        .iter()
        {
            let r = bytes_to_bigrational_between_0_and_1(&input[..]);

            if &input[..2] == vec![0x00, 0xFF] {
                assert!(Ratio::new(BigUint::from(1 as u32), BigUint::from(512 as u32)) <= r);
                assert!(Ratio::new(BigUint::from(1 as u32), BigUint::from(256 as u32)) > r);
            }

            let rb = bigrational_between_0_and_1_to_bytes(&r);
            assert_eq!(&rb[0..input.len()], &input[..]);
        }

        assert!(leading_zeroes(&from_float(0.0).unwrap()) == None);
        assert!(leading_zeroes(&from_float(0.03124).unwrap()) == Some(5));
        assert!(leading_zeroes(&from_float(0.03125).unwrap()) == Some(4));
        assert!(leading_zeroes(&from_float(0.0624).unwrap()) == Some(4));
        assert!(leading_zeroes(&from_float(0.0625).unwrap()) == Some(3));
        assert!(leading_zeroes(&from_float(0.124).unwrap()) == Some(3));
        assert!(leading_zeroes(&from_float(0.125).unwrap()) == Some(2));
        assert!(leading_zeroes(&from_float(0.13109).unwrap()) == Some(2));
        assert!(leading_zeroes(&from_float(0.249).unwrap()) == Some(2));
        assert!(leading_zeroes(&from_float(0.25).unwrap()) == Some(1));
        assert!(leading_zeroes(&from_float(0.3).unwrap()) == Some(1));
        assert!(leading_zeroes(&from_float(0.5).unwrap()) == Some(0));
        assert!(leading_zeroes(&from_float(0.56).unwrap()) == Some(0));
        assert!(leading_zeroes(&from_float(0.61429).unwrap()) == Some(0));
        assert!(leading_zeroes(&from_float(0.68).unwrap()) == Some(0));
        assert!(leading_zeroes(&from_float(0.999).unwrap()) == Some(0));
    }

    #[test]
    fn grammarfile_unknown_version() {
        let simple = r#"
---
version: "unicorn"
rules:
  0:
    - 0.25 0 1
    - 0.75 1"#;
        let parsed: Result<SCFG, anyhow::Error> = parse_grammar(simple);
        assert_matches!(parsed, Err(..));
    }

    #[test]
    fn grammarfile_v1_parser() {
        let simple = r#"
---
version: "0.0.1"
rules:
  0:
    - 0.25 0 1
    - 0.75 1
  1:
    uniform:
    - >
      "()"
    - >
      "(" 0 ")"
  2:
    uniform-choice: >
      "a" "b" "c""#;
        let parsed: Result<SCFG, anyhow::Error> = parse_grammar(simple);
        assert_matches!(parsed, Ok(..));
        if let Ok(grammar) = parsed {
            assert!(grammar.rules.keys().len() == 3);
        }
    }

    #[test]
    fn grammarfile_parser() {
        let simple = r#"
---
rules:
  0:
    - 0.5 0 1
    - 0.5 1
  1:
    - 0.5 "()"
    - 0.5 "(" 0 ")""#;
        let parsed: Result<SCFG, anyhow::Error> = parse_grammar(simple);
        assert_matches!(parsed, Ok(..));
        if let Ok(grammar) = parsed {
            assert!(grammar.rules.keys().len() == 2);
        }
    }

    #[test]
    fn grammarfile_easy2() {
        let simple = r#"
---
rules:
    0:
      - 0.96875 1 0
      - 0.03125 1 2
    1:
      - 0.125 "0"
      - 0.125 "1"
      - 0.125 "2"
      - 0.125 "3"
      - 0.125 "4"
      - 0.125 "5"
      - 0.125 "6"
      - 0.125 "7"
    2:
      - 1.0 ",""#;
        let parsed: Result<SCFG, anyhow::Error> = parse_grammar(simple);
        assert_matches!(parsed, Ok(..));
        if let Ok(grammar) = parsed {
            assert!(grammar.rules.keys().len() == 3);
            assert!(syntactically_validate_grammar(&grammar) == ValidationResult::Ok);
        }
    }

    #[test]
    fn grammarfile_parser_ungood_fractions() {
        let with_fractions = r#"
---
rules:
  0:
    - 1/2 0 1
    - 1/2 1
  1:
    - 1/3 "()"
    - 2/3 "(" 0 ")""#;
        let parsed_with_fractions: Result<SCFG, anyhow::Error> = parse_grammar(with_fractions);
        assert_matches!(parsed_with_fractions, Ok(..));
        // println!("{:#?}", parsed_with_fractions);
        if let Ok(with_fractions) = parsed_with_fractions {
            assert!(with_fractions.rules.keys().len() == 2);
        }
    }

    #[test]
    fn grammarfile_parser_fractions() {
        let with_decimals = r#"
---
rules:
  0:
    - 0.5 0 1
    - 0.5 1
  1:
    - 0.75 "()"
    - 0.25 "(" 0 ")""#;
        let with_fractions = r#"
---
rules:
  0:
    - 1/2 0 1
    - 1/2 1
  1:
    - 3/4 "()"
    - 1/4 "(" 0 ")""#;
        let parsed_with_decimals: Result<SCFG, anyhow::Error> = parse_grammar(with_decimals);
        let parsed_with_fractions: Result<SCFG, anyhow::Error> = parse_grammar(with_fractions);
        assert_matches!(parsed_with_fractions, Ok(..));
        if let (Ok(with_decimals), Ok(with_fractions)) =
            (parsed_with_decimals, parsed_with_fractions)
        {
            assert!(with_decimals.rules.keys().len() == 2);
            assert!(with_fractions.rules.keys().len() == 2);
            // println!("{:?}", with_decimals);
            // println!("{:?}", with_fractions);
            // assert!(with_decimals == with_fractions);
        }
    }

    #[test]
    fn grammarfile_parser_error() {
        let simple = r#"
---
rules:
  0:
    - 0.5 0 1
    -
  1:
    - 0.5 "()"
    - 0.5 "(" 0 ")""#;
        let parsed: Result<SCFG, anyhow::Error> = parse_grammar(simple);
        assert_matches!(parsed, Err(..));
    }

    #[test]
    fn grammar_validator() {
        let simple = r#"
---
rules:
  0:
    - 0.5 0 1
    - 0.5 1
  1:
    - 0.5 "()"
    - 0.5 "(" 0 ")""#;
        let parsed: Result<SCFG, anyhow::Error> = parse_grammar(simple);
        assert_matches!(parsed, Ok(..));
        if let Ok(grammar) = parsed {
            assert!(grammar.rules.keys().len() == 2);
            assert!(syntactically_validate_grammar(&grammar) == ValidationResult::Ok);
        }
    }

    #[test]
    fn grammar_validator_err_inconsistent() {
        let simple_wrong = r#"
---
rules:
  0:
    - 0.5 0 1
    - 0.25 1
  1:
    - 0.5 "()"
    - 0.5 "(" 0 ")""#;
        let parsed: Result<SCFG, anyhow::Error> = parse_grammar(simple_wrong);
        assert_matches!(parsed, Ok(..));
        if let Ok(grammar) = parsed {
            assert!(grammar.rules.keys().len() == 2);
            assert!(
                syntactically_validate_grammar(&grammar)
                    == ValidationResult::ProbabilitiesInconsistent {
                        n1: 0,
                        is: BinaryFraction::from_float_between_0_and_1(0.75).unwrap(),
                        should: BinaryFraction::from_float_between_0_and_1(1.0).unwrap()
                    }
            );
        }
    }

    #[test]
    fn grammar_validator_err_missing() {
        let simple_wrong = r#"
---
rules:
  0:
    - 0.5 0 1
    - 0.25 1"#;
        let parsed: Result<SCFG, anyhow::Error> = parse_grammar(simple_wrong);
        assert_matches!(parsed, Ok(..));
        if let Ok(grammar) = parsed {
            assert!(grammar.rules.keys().len() == 1);
            assert!(
                syntactically_validate_grammar(&grammar)
                    == ValidationResult::MissingNonterminal(0, 1)
            );
        }
    }

    #[test]
    fn grammar_validator_no_problem_loop() {
        let simple = r#"
---
rules:
  0:
    - 0.5 0 1
    - 0.5 1
  1:
    - 0.5 "(" 2 ")"
    - 0.5 "(" 0 ")"
  2:
    - 1.0 "()""#;
        let parsed: Result<SCFG, anyhow::Error> = parse_grammar(simple);
        assert_matches!(parsed, Ok(..));
        if let Ok(grammar) = parsed {
            // No other issues
            assert!(syntactically_validate_grammar(&grammar) == ValidationResult::Ok);
            assert!(detect_looping_nonterminals(&grammar).is_empty());
        }
    }

    #[test]
    fn grammar_validator_problem_loop() {
        let simple = r#"
---
rules:
  0:
    - 0.5 0 1
    - 0.5 1
  1:
    - 1.0 "(" 2 ")"
  2:
    - 0.75 2
    - 0.25 0"#;
        let parsed: Result<SCFG, anyhow::Error> = parse_grammar(simple);
        assert_matches!(parsed, Ok(..));
        if let Ok(grammar) = parsed {
            // No other issues
            assert!(syntactically_validate_grammar(&grammar) == ValidationResult::Ok);
            assert!(!detect_looping_nonterminals(&grammar).is_empty());
        }
    }
}
